<?php

namespace common\logics;

use Yii;
use common\helpers\ArrayHelper;
use common\models\shop\ShopProduct;
use common\models\freight\FreightMode;
use common\models\freight\FreightModeArea;

/**
 * 运费计算器
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class ExpressCalculator extends \common\base\Model {

    /**
     * @var array SPU商品列表
     */
    public $items = [];

    /**
     * @var string 地区码
     */
    public $areacode;

    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            ['areacode', 'string', 'max' => 6],
            ['items', 'validateItems'],
            ['areacode', 'validateAreaCode'],
            [['items'], 'safe'],
        ];
    }

    /**
     * 校验SPU商品数组
     * @param string $attribute the attribute currently being validated
     */
    public function validateItems($attribute) {
        if (!empty($this->$attribute)) {
            $this->$attribute = $this->sumFilter($this->$attribute);
        }
    }

    /**
     * 校验地区代码
     * @param string $attribute the attribute currently being validated
     */
    public function validateAreaCode($attribute) {
        $code = $this->$attribute;
        if (is_numeric($code) && strlen($code) >= 2) {
            $this->areacode = substr($code, 0, 2) . '%';
        }
    }

    /**
     * SPU商品检重数量求和
     */
    private function sumFilter($list) {
        $data = [];
        foreach ($list as $item) {
            $pid = ArrayHelper::getValue($item, 'product_id');
            if ($pid === null) {
                continue;
            }
            $nums = ArrayHelper::getValue($item, 'num', 0);
            if (isset($data[$pid])) {
                $data[$pid] += $nums;
            } else {
                $data[$pid] = $nums;
            }
        }
        return $data;
    }

    /**
     * 计算运费
     */
    public function reckon() {
        if (!$this->validate()) {
            return $this->dumpError('');
        }
        if (empty($this->items)) {
            return 0;
        }

        $spus = ShopProduct::find()
        ->alias('t')
        ->andWhere(['t.id' => array_keys($this->items)])
        ->andWhere(['!=', 't.template_id', 0])
        ->innerJoinWith('shipTemplate tpl')
        ->andWhere(['tpl.free' => 0])
        ->all();

        if (empty($spus)) {
            return 0;
        }

        $templateids = [];
        foreach ($spus as $spu) {
            $nums = $this->items[$spu->id];
            $tplid = $spu->template_id;
            if (isset($templateids[$tplid])) {
                $templateids[$tplid] += $nums;
            } else {
                $templateids[$tplid] = $nums;
            }
        }

        $areaTemp = [];
        if ($this->areacode !== null) {
            $areas = FreightModeArea::find()
            ->andWhere(['like', 'code', $this->areacode, false])
            ->andWhere(['template_id' => array_keys($templateids)])
            ->groupBy('mode_id')
            ->all();
            $areaTemp = ArrayHelper::map($areas, 'template_id', 'mode_id');
        }

        $modes = FreightMode::find()
        ->andWhere(['template_id' => array_keys($templateids)])
        ->andWhere([
            'or',
            ['parent_id' => 0],
            ['id' => $areaTemp],
        ])
        ->orderBy(['template_id' => SORT_ASC, 'parent_id' => SORT_ASC])
        ->all();

        $modeTemp = ArrayHelper::index($modes, 'template_id');

        $price = 0;
        foreach ($modeTemp as $base) {
            $nums = $templateids[$base->template_id];
            $price += $base->price;
            if ($nums - $base->quantity > 0) {
                $price += floor(($nums - $base->quantity) / $base->increase_quantity) * $base->increase_price;
            }
        }

        return $price;
    }

    /**
     * 计算运费
     */
    public function reckonCard() {
        if (!$this->validate()) {
            return $this->dumpError('');
        }

        $templateid = 2;

        $areaTemp = [];
        if ($this->areacode !== null) {
            $areas = FreightModeArea::find()
            ->andWhere(['like', 'code', $this->areacode, false])
            ->andWhere(['template_id' => $templateid])
            ->groupBy('mode_id')
            ->all();
            $areaTemp = ArrayHelper::map($areas, 'template_id', 'mode_id');
        }

        $modes = FreightMode::find()
        ->andWhere(['template_id' => $templateid])
        ->andWhere([
            'or',
            ['parent_id' => 0],
            ['id' => $areaTemp],
        ])
        ->orderBy(['template_id' => SORT_ASC, 'parent_id' => SORT_ASC])
        ->all();

        $modeTemp = ArrayHelper::index($modes, 'template_id');

        $price = 0;
        foreach ($modeTemp as $base) {
            $nums = 1;
            $price += $base->price;
            $price += floor(($nums - $base->quantity) / $base->increase_quantity) * $base->increase_price;
        }

        return $price;
    }

}
